/*
* UAE - The Un*x Amiga Emulator
*
* Event stuff
*
* Copyright 1995-2002 Bernd Schmidt
* Copyright 1995 Alessandro Bissacco
* Copyright 2000-2012 Toni Wilen
*/

#include "sysconfig.h"
#include "sysdeps.h"

#include "options.h"
#include "events.h"
#include "memory.h"
#include "newcpu.h"
#include "uae/ppc.h"
#include "x86.h"

static const int pissoff_nojit_value = 256 * CYCLE_UNIT;

unsigned long int event_cycles, nextevent, currcycle;
int is_syncline, is_syncline_end;
long cycles_to_next_event;
long max_cycles_to_next_event;
long cycles_to_hsync_event;
unsigned long start_cycles;
bool event_wait;

frame_time_t vsyncmintime, vsyncmaxtime, vsyncwaittime;
int vsynctimebase;
int event2_count;

void events_schedule (void)
{
	int i;

	unsigned long int mintime = ~0L;
	for (i = 0; i < ev_max; i++) {
		if (eventtab[i].active) {
			unsigned long int eventtime = eventtab[i].evtime - currcycle;
			if (eventtime < mintime)
				mintime = eventtime;
		}
	}
	nextevent = currcycle + mintime;
}

void do_cycles_slow (unsigned long cycles_to_add)
{
#ifdef WITH_X86
	if (x86_turbo_on) {
		execute_other_cpu_single();
	}
#endif

	if ((pissoff -= cycles_to_add) >= 0)
		return;

	cycles_to_add = -pissoff;
	pissoff = 0;

	while ((nextevent - currcycle) <= cycles_to_add) {
		int i;

		/* Keep only CPU emulation running while waiting for sync point. */
		if (is_syncline) {
			if (!vblank_found_chipset) {
				if (is_syncline > 0) {
					int rpt = read_processor_time ();
					int v = rpt - vsyncmintime;
					int v2 = rpt - is_syncline_end;
					if (v > vsynctimebase || v < -vsynctimebase) {
						v = 0;
					}
					if (v < 0 && v2 < 0 && event_wait) {

#ifdef WITH_PPC
						if (ppc_state) {
							if (is_syncline == 1) {
								uae_ppc_execute_check();
							} else {
								uae_ppc_execute_quick();
							}
						}
#endif
						if (currprefs.cachesize)
							pissoff = pissoff_value;
						else
							pissoff = pissoff_nojit_value;
						return;
					}
				} else if (is_syncline < 0) {
					int rpt = read_processor_time ();
					int v = rpt - is_syncline_end;
					if (v < 0 && event_wait) {

#ifdef WITH_PPC
						if (ppc_state) {
							uae_ppc_execute_check();
						}
#endif
						if (currprefs.cachesize)
							pissoff = pissoff_value;
						else
							pissoff = pissoff_nojit_value;
						return;
					}
				}
			}
			is_syncline = 0;
		}

		cycles_to_add -= nextevent - currcycle;
		currcycle = nextevent;

		for (i = 0; i < ev_max; i++) {
			if (eventtab[i].active && eventtab[i].evtime == currcycle) {
				if (eventtab[i].handler == NULL) {
					gui_message(_T("eventtab[%d].handler is null!\n"), i);
					eventtab[i].active = 0;
				} else {
					(*eventtab[i].handler)();
				}
			}
		}
		events_schedule ();


	}
	currcycle += cycles_to_add;
}

void MISC_handler (void)
{
	static bool dorecheck;
	bool recheck;
	int i;
	evt mintime;
	evt ct = get_cycles ();
	static int recursive;

	if (recursive) {
		dorecheck = true;
		return;
	}
	recursive++;
	eventtab[ev_misc].active = 0;
	recheck = true;
	while (recheck) {
		recheck = false;
		mintime = ~0L;
		for (i = 0; i < ev2_max; i++) {
			if (eventtab2[i].active) {
				if (eventtab2[i].evtime == ct) {
					eventtab2[i].active = false;
					event2_count--;
					eventtab2[i].handler (eventtab2[i].data);
					if (dorecheck || eventtab2[i].active) {
						recheck = true;
						dorecheck = false;
					}
				} else {
					evt eventtime = eventtab2[i].evtime - ct;
					if (eventtime < mintime)
						mintime = eventtime;
				}
			}
		}
	}
	if (mintime != ~0UL) {
		eventtab[ev_misc].active = true;
		eventtab[ev_misc].oldcycles = ct;
		eventtab[ev_misc].evtime = ct + mintime;
		events_schedule ();
	}
	recursive--;
}


void event2_newevent_xx (int no, evt t, uae_u32 data, evfunc2 func)
{
	evt et;
	static int next = ev2_misc;

	et = t + get_cycles ();
	if (no < 0) {
		no = next;
		for (;;) {
			if (!eventtab2[no].active) {
				event2_count++;
				break;
			}
			if (eventtab2[no].evtime == et && eventtab2[no].handler == func && eventtab2[no].data == data)
				break;
			no++;
			if (no == ev2_max)
				no = ev2_misc;
			if (no == next) {
				write_log (_T("out of event2's!\n"));
				return;
			}
		}
		next = no;
	}
	eventtab2[no].active = true;
	eventtab2[no].evtime = et;
	eventtab2[no].handler = func;
	eventtab2[no].data = data;
	MISC_handler ();
}

int current_hpos (void)
{
	int hp = current_hpos_safe ();
	if (hp < 0 || hp > 256) {
		gui_message(_T("hpos = %d!?\n"), hp);
		hp = 0;
	}
	return hp;
}

